package com.yuelao.yuelao_backend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuelao.yuelao_backend.common.BizException;
import com.yuelao.yuelao_backend.common.ErrorCode;
import com.yuelao.yuelao_backend.dto.MiningPoolDTO;
import com.yuelao.yuelao_backend.dto.MiningStatsDTO;
import com.yuelao.yuelao_backend.dto.UserMiningInfoDTO;
import com.yuelao.yuelao_backend.entity.MiningPool;
import com.yuelao.yuelao_backend.entity.User;
import com.yuelao.yuelao_backend.entity.UserStaking;
import com.yuelao.yuelao_backend.mapper.MiningPoolMapper;
import com.yuelao.yuelao_backend.mapper.UserStakingMapper;
import com.yuelao.yuelao_backend.service.AchievementService;
import com.yuelao.yuelao_backend.service.CoinService;
import com.yuelao.yuelao_backend.service.MiningService;
import com.yuelao.yuelao_backend.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class MiningServiceImpl extends ServiceImpl<MiningPoolMapper, MiningPool> implements MiningService {

    @Resource
    private UserStakingMapper userStakingMapper;

    @Resource
    private UserService userService;

    @Resource
    private CoinService coinService;
    
    @Resource
    private AchievementService achievementService;

    @Override
    public List<MiningPoolDTO> getMiningPools() {
        LambdaQueryWrapper<MiningPool> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MiningPool::getStatus, "active");
        List<MiningPool> pools = list(queryWrapper);
        return pools.stream()
                .map(this::convertToMiningPoolDTO)
                .collect(Collectors.toList());
    }

    @Override
    public UserMiningInfoDTO getUserMiningInfo(Long userId) {
        User user = userService.getById(userId);
        if (user == null) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }

        LambdaQueryWrapper<UserStaking> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserStaking::getUserId, userId)
                .eq(UserStaking::getStatus, "active");
        List<UserStaking> userStakings = userStakingMapper.selectList(queryWrapper);

        UserMiningInfoDTO dto = new UserMiningInfoDTO();
        dto.setUserId(userId);
        dto.setTotalStakedAmount(userStakings.stream()
                .map(UserStaking::getStakedAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        dto.setTotalEarnedRewards(userStakings.stream()
                .map(UserStaking::getEarnedRewards)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        dto.setActiveStakings(userStakings.stream()
                .map(staking -> {
                    MiningPool pool = getById(staking.getPoolId());
                    UserMiningInfoDTO.StakingDetail detail = new UserMiningInfoDTO.StakingDetail();
                    BeanUtils.copyProperties(staking, detail);
                    if (pool != null) {
                        detail.setPoolName(pool.getName());
                        detail.setTokenSymbol(pool.getTokenSymbol());
                        detail.setApr(pool.getApr());
                    }
                    // 计算实时未领取收益 (简化计算，实际可能更复杂)
                    BigDecimal pendingRewards = calculatePendingRewards(staking, pool);
                    detail.setPendingRewards(pendingRewards);
                    return detail;
                })
                .collect(Collectors.toList()));
        return dto;
    }

    private BigDecimal calculatePendingRewards(UserStaking staking, MiningPool pool) {
        if (pool == null || pool.getApr() == null || staking.getStakedAmount() == null) {
            return BigDecimal.ZERO;
        }
        // 假设按秒计算收益
        long secondsSinceLastClaim = Duration.between(staking.getLastClaimTime(), LocalDateTime.now()).getSeconds();
        BigDecimal dailyRate = pool.getApr().divide(new BigDecimal(365), 10, RoundingMode.HALF_UP);
        BigDecimal hourlyRate = dailyRate.divide(new BigDecimal(24), 10, RoundingMode.HALF_UP);
        BigDecimal minuteRate = hourlyRate.divide(new BigDecimal(60), 10, RoundingMode.HALF_UP);
        BigDecimal secondRate = minuteRate.divide(new BigDecimal(60), 10, RoundingMode.HALF_UP);

        BigDecimal rewardsPerSecond = staking.getStakedAmount().multiply(secondRate);
        return rewardsPerSecond.multiply(new BigDecimal(secondsSinceLastClaim)).setScale(8, RoundingMode.HALF_UP);
    }

    @Override
    @Transactional
    public boolean stakeToPool(Long userId, Long poolId, BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "质押金额必须大于0");
        }
        MiningPool pool = getById(poolId);
        if (pool == null || !"active".equals(pool.getStatus())) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "挖矿池不存在或未激活");
        }
        User user = userService.getById(userId);
        if (user == null) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }

        // 扣除用户爱心币 (假设质押的是爱心币)
        coinService.spendCoins(userId, amount.intValue(), "mining", "质押到挖矿池", 
            "质押到挖矿池: " + pool.getName(), poolId, "mining_stake");

        UserStaking userStaking = new UserStaking();
        userStaking.setUserId(userId);
        userStaking.setPoolId(poolId);
        userStaking.setStakedAmount(amount);
        userStaking.setEarnedRewards(BigDecimal.ZERO);
        userStaking.setStakeTime(LocalDateTime.now());
        userStaking.setLastClaimTime(LocalDateTime.now());
        userStaking.setStatus("active");
        boolean saved = userStakingMapper.insert(userStaking) > 0;
        
        // 检查挖矿相关成就
        if (saved) {
            achievementService.checkMiningAchievements(userId, 0); // 首次挖矿
            achievementService.checkStakingAchievements(userId);
        }
        
        return saved;
    }

    @Override
    @Transactional
    public BigDecimal claimRewards(Long userId, Long stakingId) {
        UserStaking staking = userStakingMapper.selectById(stakingId);
        if (staking == null || !staking.getUserId().equals(userId) || !"active".equals(staking.getStatus())) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "质押记录不存在或不属于当前用户");
        }
        MiningPool pool = getById(staking.getPoolId());
        if (pool == null) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "关联挖矿池不存在");
        }

        BigDecimal pendingRewards = calculatePendingRewards(staking, pool);
        if (pendingRewards.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException(ErrorCode.OPERATION_ERROR, "没有可领取的奖励");
        }

        // 增加用户爱心币 (假设奖励是爱心币)
        coinService.addCoins(userId, pendingRewards.intValue(), "mining", "领取挖矿奖励", 
            "领取挖矿奖励", stakingId, "mining_rewards");

        staking.setEarnedRewards(staking.getEarnedRewards().add(pendingRewards));
        staking.setLastClaimTime(LocalDateTime.now());
        userStakingMapper.updateById(staking);
        
        // 检查挖矿收益成就
        achievementService.checkMiningAchievements(userId, pendingRewards.intValue());
        
        return pendingRewards;
    }

    @Override
    @Transactional
    public BigDecimal unstakeFromPool(Long userId, Long stakingId, BigDecimal amount) {
        UserStaking staking = userStakingMapper.selectById(stakingId);
        if (staking == null || !staking.getUserId().equals(userId) || !"active".equals(staking.getStatus())) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "质押记录不存在或不属于当前用户");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0 || amount.compareTo(staking.getStakedAmount()) > 0) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "提取金额无效");
        }

        // 提取质押，返还爱心币
        coinService.addCoins(userId, amount.intValue(), "mining", "提取挖矿质押", 
            "提取挖矿质押", stakingId, "mining_unstake");

        staking.setStakedAmount(staking.getStakedAmount().subtract(amount));
        if (staking.getStakedAmount().compareTo(BigDecimal.ZERO) == 0) {
            staking.setStatus("unstaked"); // 全部提取则标记为已提取
        }
        userStakingMapper.updateById(staking);
        return amount;
    }

    @Override
    public MiningStatsDTO getMiningStats() {
        MiningStatsDTO dto = new MiningStatsDTO();
        // 模拟数据
        dto.setTotalStakedValue(new BigDecimal("1000000.00"));
        dto.setTotalRewardsDistributed(new BigDecimal("50000.00"));
        dto.setActivePoolCount(count(new LambdaQueryWrapper<MiningPool>().eq(MiningPool::getStatus, "active")));
        return dto;
    }

    @Override
    public BigDecimal calculateRewards(Long poolId, BigDecimal amount, Integer duration) {
        MiningPool pool = getById(poolId);
        if (pool == null || pool.getApr() == null || pool.getApr().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "挖矿池不存在或APR无效");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0 || duration <= 0) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "金额和时长必须大于0");
        }

        // 每日收益率 = APR / 365
        BigDecimal dailyRate = pool.getApr().divide(new BigDecimal(365), 10, RoundingMode.HALF_UP);
        // 预计收益 = 质押金额 * 每日收益率 * 质押时长
        BigDecimal estimatedRewards = amount.multiply(dailyRate).multiply(new BigDecimal(duration));
        return estimatedRewards.setScale(8, RoundingMode.HALF_UP);
    }

    @Override
    public List<String> getMiningEvents() {
        // 模拟挖矿活动信息
        return Arrays.asList(
                "新矿池上线：质押USDT赢取YUELAO代币，APR高达120%！",
                "挖矿收益翻倍活动，限时7天！"
        );
    }

    @Override
    public MiningPoolDTO convertToMiningPoolDTO(MiningPool miningPool) {
        if (miningPool == null) {
            return null;
        }
        MiningPoolDTO dto = new MiningPoolDTO();
        BeanUtils.copyProperties(miningPool, dto);
        return dto;
    }
}